using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Object = UnityEngine.Object;

namespace Miao
{
    /// <summary>
    /// 协程启动器
    /// </summary>
    public static class CoroutineStarter
    {
        private static int runningNumber;
        private static int waitAddNumber;
        private static int waitRemoveNumber;

        private static readonly List<CoroutineInternal> waitAddList = new List<CoroutineInternal>(4);
        private static readonly List<CoroutineInternal> coroutineList = new List<CoroutineInternal>(4);
        private static readonly List<CoroutineInternal> waitRemoveList = new List<CoroutineInternal>(4);

        static CoroutineStarter()
        {
            GameObject updaterGameObject = new GameObject("CoroutineUpdater") { hideFlags = HideFlags.HideInHierarchy };
            CoroutineUpdater coroutineUpdater = updaterGameObject.AddComponent<CoroutineUpdater>();
            Object.DontDestroyOnLoad(updaterGameObject);
            coroutineUpdater.updateFunction = Update;
        }

        /// <summary>
        /// 启动指定的 <see cref="IEnumerator"/> 协程
        /// </summary>
        /// <param name="enumerator"></param>
        /// <returns></returns>
        public static RunningCoroutine Start(IEnumerator enumerator)
        {
            waitAddNumber++;
            CoroutineInternal coroutineInternal = new CoroutineInternal(enumerator);
            waitAddList.Add(coroutineInternal);
            RunningCoroutine coroutine = new RunningCoroutine();
            coroutine.coroutineInternal = coroutineInternal;
            coroutineInternal.runningCoroutine = coroutine;
            return coroutine;
        }
        /// <summary>
        /// 启动带有 <see cref="IEnumerator"/> 返回值的协程函数
        /// </summary>
        /// <param name="enumeratorFunction"></param>
        /// <returns></returns>
        public static RunningCoroutine Start(Func<IEnumerator> enumeratorFunction)
        {
            waitAddNumber++;
            CoroutineInternal coroutineInternal = new CoroutineInternal(enumeratorFunction());
            waitAddList.Add(coroutineInternal);
            RunningCoroutine coroutine = new RunningCoroutine();
            coroutine.coroutineInternal = coroutineInternal;
            coroutineInternal.runningCoroutine = coroutine;
            return coroutine;
        }
        /// <summary>
        /// 停止协程 <paramref name="runningCoroutine"/>
        /// </summary>
        /// <param name="runningCoroutine"></param>
        public static void Stop(RunningCoroutine runningCoroutine)
        {
            runningCoroutine.Stop();
        }

        internal static void AddInternal(CoroutineInternal coroutineInternal)
        {
            waitAddNumber++;
            waitAddList.Add(coroutineInternal);
        }
        internal static void RemoveInternal(CoroutineInternal coroutineInternal)
        {
            waitRemoveNumber++;
            waitRemoveList.Add(coroutineInternal);
        }

        private static void Update()
        {
            //保证先添加后移除
            //这是为了避免当子协程被添加而其父协程又刚好在同一帧内移除时无法移除子协程的问题
            AddCoroutine();
            RemoveCoroutine();
            UpdateCoroutine();
        }
        private static void AddCoroutine()
        {
            if (waitAddNumber == 0) return;
            foreach (var coroutine in waitAddList)
            {
                runningNumber++;
                coroutineList.Add(coroutine);
            }
            waitAddList.Clear();
            waitAddNumber = 0;
        }
        private static void RemoveCoroutine()
        {
            if (waitRemoveNumber == 0) return;
            foreach (var coroutine in waitRemoveList)
            {
                runningNumber--;
                if (coroutine.parent) coroutine.parent.child = null;
                coroutineList.Remove(coroutine);
                CoroutineInternal child = coroutine.child;//移除嵌套协程
                while (child)
                {
                    runningNumber--;
                    child.runningCoroutine?.ParentStop();
                    coroutineList.Remove(child);
                    child = child.child;
                }
            }
            waitRemoveList.Clear();
            waitRemoveNumber = 0;
        }
        private static void UpdateCoroutine()
        {
            if (runningNumber == 0) return;
            for (int i = 0; i < runningNumber; i++)
            {
                CoroutineInternal coroutineInternal = coroutineList[i];
                if (!coroutineInternal.Update()) continue;
                coroutineInternal.runningCoroutine?.Complete();//子协程是没有持有外部句柄的, 所以要判断 runningCoroutine 是否为空
                RemoveInternal(coroutineInternal);
            }
        }
    }
}